# encoding: utf-8
"""
UCloud resources
============
"""

import logging, os, pexpect, re, requests
import traceback

from flask_restplus import Resource

from app.extensions import db
from app.extensions.api import Namespace
from app.modules.thread.thread import AddThreadTask
from app.modules.common.autoTest import TestMessage
from .models import AutoTest

log = logging.getLogger(__name__)  # pylint: disable=invalid-name
api = Namespace('autotest', description = "自动测试")  # pylint: disable=invalid-name

from . import parameters
from app.modules.common.dbUtil import DbCommit
from app.modules.common.schemas import BaseSchema, BaseDataSchema
from config import BeetleConfig
from app.modules.notify import notifier


@api.route('/init')
class InitCases(Resource):
    @api.parameters(parameters.InitParameters())
    @api.response(BaseSchema(many = False))
    def post(self, args):
        """
        初始化导入测试用例
        """
        log.info("============== start init test case ==============")
        _path = '%s/01.测试用例' % args['path']
        _isClean = args['isClean']
        if not os.path.exists(_path):
            log.info("测试用例路径 %s 不存在" % _path)
            return {'success': False, 'msg': "测试用例路径 %s 不存在" % _path}

        if _isClean is not None and _isClean:
            deleteCaseBase(_path)

        return InitCaseBase(_path)


def deleteCaseBase(_path):
    """
    删除数据库里的case  当case对应的文件或者内容不存在时 删除   级联删除
    :param _path:
    :return:
    """
    _root = AutoTest.query.filter_by(parent_id = '0').first()
    if _root is None:
        return
    deleteCase(_path, _root, False)
    return DbCommit()


def deleteCase(_path, _case, _isDel = False):
    """
    判断case是否存在  存在则继续判断子case  不存在则删除case以及子case
    :param _path:
    :param _case:
    :param _isDel: 为true时  直接删除case
    :return:
    """
    _list = AutoTest.query.filter_by(parent_id = _case.id).all()

    if _isDel:
        db.session.delete(_case)
        for _c in _list:
            deleteCase(_path, _c, True)
        return

    # 测试用例  判断文件中是否存在
    if _case.is_case == '1':
        file_object = open('%s%s' % (_path, _case.path), 'r')
        all_the_text = file_object.read()
        if not str(all_the_text).__contains__(_case.name):
            db.session.delete(_case)
        return

    # 测试套  判断文件是否存在
    if not os.path.exists('%s%s' % (_path, _case.path)):
        db.session.delete(_case)
        _isDel = True
    for _c in _list:
        deleteCase(_path, _c, _isDel)


def InitCaseBase(_path):
    # 1、添加根节点
    _case = insertCase('测试用例', '0', '0', '0', '0', None)
    # 2、遍历目录
    _list = os.listdir(_path)
    for file in _list:
        InitCase(_case.id, file, _list.index(file), os.path.join(_path, file))

    return DbCommit()


@api.route('/getTree')
class GetCaseTree(Resource):
    @api.parameters(parameters.GetParameters())
    @api.response(BaseDataSchema(many = False))
    def get(self, args):
        """
        获取测试用例的树形结构
        """
        return {'success': True, 'msg': "", 'data': getCaseTree()}


@api.route('/do')
class DoAutoTest(Resource):
    @api.parameters(parameters.DoAutoTestParameters())
    @api.response(BaseSchema(many = False))
    def post(self, args):
        """
        执行自动测试
        """
        _tags = args['tags']
        _cases = args['cases']
        _id = args['id']
        log.info("========= do auto test .tags=%s . cases=%s ==========" % (_tags, _cases))
        _obj = getTestParam(_cases)
        cases = _obj['cases']
        tags = None
        if _tags is not None:
            tags = str(_tags).split(',')
        AddThreadTask(doTest, _id, _obj['root_case'], tags, cases)

        return {'success': True, 'msg': ""}


def InitCase(parent_id, root_case, index, _path):
    if str(_path).endswith('__init__.txt') or str(_path).endswith('Store') or str(_path).endswith('.svn'):
        return
    _case = insertCase(os.path.basename(_path), parent_id, root_case, index, '0', _path)
    if os.path.isfile(_path):
        # 读取文件，保存用例
        file_object = open(_path, 'r')
        _flag = False
        _index = 0
        while 1:
            line = file_object.readline()
            if not line:
                break
            line = line.replace('\r', '').replace('\n', '')
            if str(line).startswith('*** Test Cases ***'):
                _flag = True
                continue
            if _flag and line != '' and not str(line).startswith(' '):
                insertCase(line, _case.id, root_case, _index, '1', _path)
                _index = _index + 1
        return
    _list = os.listdir(_path)
    for file in _list:
        InitCase(_case.id, root_case, _list.index(file), os.path.join(_path, file))
    pass


def insertCase(name, parent_id, root_case, index, is_case, path):
    # 如果用例已经存在，则不新增
    _info = AutoTest.query.filter_by(name = name.replace("_", " ").replace('.txt', ''), parent_id = parent_id).first()
    if _info is not None:
        log.info(_info.to_dict())
        return _info

    if path is not None:
        path = str(path).replace('%s/01.测试用例' % BeetleConfig.AUTO_TEST_CASE_PATH, '')
    _case = AutoTest(name = name.replace("_", " ").replace('.txt', ''), parent_id = parent_id,
                     root_case = root_case.replace("_", " "), index = index, is_case = is_case, path = path)
    log.info(_case.to_dict())
    db.session.add(_case)
    return _case


def getCaseTree():
    _roots = AutoTest.query.filter_by(parent_id = '0').order_by(AutoTest.index.asc()).all()
    if _roots is None or len(_roots) == 0:
        return []
    _l = []
    for _root in _roots:
        _root.childs = getCaseByParent(_root)
        _l.append(_root.to_dict())
    return _l


def getCaseByParent(_case):
    _l = []
    _list = AutoTest.query.filter_by(parent_id = _case.id).order_by(AutoTest.index.asc()).all()
    if _list is None or len(_list) == 0:
        return _l
    for _node in _list:
        if _node.is_case != '1':
            _node.childs = getCaseByParent(_node)
            log.info(_node.to_dict())
        _l.append(_node.to_dict())
    return _l


def getTestParam(cases):
    """
    获取执行用例的参数
    :return:
    """
    if cases is None or cases == '':
        return {'root_case': '无', 'cases': []}

    _list = AutoTest.query.filter(AutoTest.id.in_(str(cases).split(','))).all()
    if _list is None or len(_list) == 0:
        _msg = "没有找到对应的测试用例.cases=%s" % cases
        log.error("============== %s ==============" % _msg)
        return {'root_case': '无', 'cases': []}

    _root_case = None
    # case 名称列表
    _l = []
    for _case in _list:
        if _root_case is None:
            _root_case = _case.root_case
        if _case.root_case not in _root_case:
            _root_case = '%s,%s' % (_root_case, _case.root_case)
        getTestCase(_case, _l)
    _data = {'root_case': _root_case, 'cases': _l}
    log.info('app.modules.autotest.resources.getTestParam _data=%s' % _data)
    return _data


def getTestCase(_case, _list):
    if _case.is_case == '1':
        if _case.name not in _list:
            _list.append(_case.name)
    else:
        for _c in AutoTest.query.filter_by(parent_id = _case.id).all():
            getTestCase(_c, _list)


def doTest(id, root_case, tags, cases):
    _sh = 'pybot -d %s/%s ' % (BeetleConfig.AUTO_TEST_LOG_PATH, id)
    if tags is not None and len(tags) != 0:
        _sh = '%s --include ' % _sh
        for _tag in tags:
            _sh = '%s"%s"OR' % (_sh, _tag)
        _sh = '%s ' % _sh
        _sh = str(_sh).replace('OR ', ' ')
    for _case in cases:
        _sh = "%s --test '%s' " % (_sh, _case)

    _sh = '%s %s/01.测试用例' % (_sh, BeetleConfig.AUTO_TEST_CASE_PATH)

    log.info('执行自动测试，%s' % _sh)

    process = pexpect.spawn(_sh)
    while process.isalive():
        output = process.readline()
        notifier.send_message(TestMessage(output))

    notifier.send_message(TestMessage("finish"))

    try:
        # 匹配日志文件，获取测试结果
        log_path = '%s/%s/log.html' % (BeetleConfig.AUTO_TEST_LOG_PATH, id)
        file_object = open(log_path, 'r')
        all_the_text = file_object.read()

        result = re.sub("([\\s\\S]*)(\\{)([\\S\\s]*)(All Tests)([\\s\\S]*?)(})([\\s\\S]*)", "\\2\\3\\4\\5\\6",
                        all_the_text)
        notifier.send_message(TestMessage('reportPath:/%s/report.html' % id))
        notifier.send_message(TestMessage('PASS:%s' % eval(result)['pass']))
        notifier.send_message(TestMessage('FAIL:%s' % eval(result)['fail']))
        postTestResult(id, root_case, eval(result)['pass'], eval(result)['fail'], '/%s/report.html' % id, None)
    except BaseException as e:
        log.error(traceback.format_exc())
        notifier.send_message(TestMessage('ERROR:%s' % e))
        postTestResult(id, root_case, None, None, None, e)


# 把测试结果通知给beetle-plan
def postTestResult(id, root_case, success, fail, report_path, desc):
    _data = {}
    if report_path is not None:
        _data['report_path'] = report_path
    if success is not None:
        _data['success'] = success
    if root_case is not None:
        _data['root_case'] = root_case
    if fail is not None:
        _data['fail'] = fail
    if id is not None:
        _data['id'] = id
    if desc is not None:
        _data['desc'] = desc

    _url = '%s/autotest/update' % BeetleConfig.BEETLE_PLAN_URL
    _rtn = requests.post(url = _url, data = _data).json()
    log.info("app.modules.autotest.resources.postTestResult %s" % _rtn)
